package pres.seanlang.utility;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 	线程工具集
 *
 * @author SeanLang
 */
public class ThreadUtils {

    private static final String TAG = "ThreadUtils";
	
	protected ThreadUtils() { throw new UnsupportedOperationException("u can't instantiate me..."); }

    private static ThreadPoolExecutor mService;
    private static ConcurrentHashMap<Integer, ScheduledFuture<?>> mRunningTasks = new ConcurrentHashMap<>();
    private static ScheduledThreadPoolExecutor mScheduledService;

    static {
        int CUP_COUNT = Runtime.getRuntime().availableProcessors();
        mService = new ThreadPoolExecutor(CUP_COUNT + 1, 50,
                30L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                new UtilsThreadFactory("Utility[TPool]"));
        mScheduledService = new ScheduledThreadPoolExecutor(3,
                new UtilsThreadFactory("Utility[TScheduledPool]"));
    }
    
    /**
     * 休眠
     *  简化代码中的try的语句
     * @param ms
     */
    public static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException e) { e.printStackTrace(); }
    }

    /**
     *  后台运行Task
     * @param task
     */
    public static void execute(Runnable task) {
        mService.execute(task);
    }

    /**
     *  延迟运行Task
     * @param task
     * @param delayMs
     */
    public static void executeDelayed(Runnable task, long delayMs) {
        ScheduledFuture<?> future = mScheduledService.schedule(new ProxyRunnable(task), delayMs, TimeUnit.MILLISECONDS);
        mRunningTasks.put(task.hashCode(), future);
    }

    /**
     *  延迟并以固定的频率执行Task <br>
     *      执行的规律. startDelayMs, startDelayMs+periodMs, startDelayMs+2*periodMs, .......
     * @param task
     * @param startDelayMs      开始执行的延迟时间
     * @param periodMs          固定频率的延迟时间
     * @return
     */
    public static void executeAtFixedRate(Runnable task, long startDelayMs, long periodMs) {
        ScheduledFuture<?> future = mScheduledService.scheduleAtFixedRate(new ProxyRunnable(task), startDelayMs, periodMs, TimeUnit.MILLISECONDS);
        synchronized (mRunningTasks) { mRunningTasks.put(task.hashCode(), future); }
    }

    /**
     *   延迟并以固定的频率执行Task。 <br>
     *      首先启用的定期动作，随后在一个执行的终止和下一个执行的开始之间给定的延迟。
     * @param task
     * @param startDelayMs  开始执行的延迟时间
     * @param periodMs  固定频率的延迟时间
     * @return
     */
    public static void executeWithFixedDelay(Runnable task, long startDelayMs, long periodMs) {
        ScheduledFuture<?> future = mScheduledService.scheduleWithFixedDelay(new ProxyRunnable(task), startDelayMs, periodMs, TimeUnit.MILLISECONDS);
        synchronized (mRunningTasks) { mRunningTasks.put(task.hashCode(), future); }
    }

    /**
     *  移除还未执行的任务.
     * @param task
     */
    public static boolean remove(Runnable task) {
        ScheduledFuture<?> future = null;
        int key = task.hashCode();
        synchronized (mRunningTasks) { future = mRunningTasks.get(key); }
        if(future != null) {
            future.cancel(true);
            synchronized (mRunningTasks) { mRunningTasks.remove(key); }
            //System.out.println("移除 " + key);
            return true;
        } else {
            //System.err.println("从ScheduledService中移除失败");
            System.err.println(TAG +" Cancel scheduled task failed. id=" + key);
        }

        return false;
    }

    private static final class UtilsThreadFactory extends AtomicInteger implements ThreadFactory, Thread.UncaughtExceptionHandler {
        private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
        //private final ThreadGroup group;
        private final String namePrefix;

        UtilsThreadFactory(String prefix) {
            //SecurityManager s = System.getSecurityManager();
            //group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
            namePrefix = prefix + "-pool-" + POOL_NUMBER.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, namePrefix + getAndIncrement());
            thread.setUncaughtExceptionHandler(this);
            thread.setDaemon(false);

            /*if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);*/
            return thread;
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.err.println(TAG + " " + e);
        }
    }

    private static class ProxyRunnable implements Runnable {
        private Runnable target;

        ProxyRunnable(Runnable task) {
            this.target = task;
        }

        @Override
        public void run() {
            if(target != null) {
                target.run();
                synchronized (mRunningTasks) { mRunningTasks.remove(target.hashCode()); }
            }
        }
    }
}
